package org.jeecg.modules.hc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.zhyd.oauth.utils.UuidUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.DySmsEnum;
import org.jeecg.common.util.DySmsHelper;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.hc.constant.RedisConstant;
import org.jeecg.modules.hc.entity.HcSiteAddressDetails;
import org.jeecg.modules.hc.entity.HcUser;
import org.jeecg.modules.hc.entity.HcWallet;
import org.jeecg.modules.hc.mapper.HcUserMapper;
import org.jeecg.modules.hc.pcWxPay.config.WeChatConfig;
import org.jeecg.modules.hc.service.IHcSiteAddressDetailsService;
import org.jeecg.modules.hc.service.IHcUserService;
import org.jeecg.modules.hc.service.IHcWalletService;
import org.jeecg.modules.miniAppCustomerEndPoint.entity.JxUserDo;
import org.jeecg.modules.miniAppCustomerEndPoint.entity.MiniAppSessoinEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;

/**
 * @Description: 用户
 * @Author: jeecg-boot
 * @Date: 2022-12-13
 * @Version: V1.0
 */
@Service
@Slf4j
public class HcUserServiceImpl extends ServiceImpl<HcUserMapper, HcUser> implements IHcUserService {
    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private WxMpService mpService;

    @Autowired
    private IHcSiteAddressDetailsService hcSiteAddressDetailsService;

    @Autowired
    private IHcWalletService hcWalletService;

    @Value("${wx.mp.app-id}")
    private String appId;

    @Value("${wx.mp.secret}")
    private String appSecret;

    public void setWalletBalance(HcUser hcUser) {
        HcWallet hcWallet = hcWalletService.getOne(new QueryWrapper<HcWallet>()
                .select("ifnull(sum(AMOUNT), 0) as AMOUNT")
                .eq("USER_ID", hcUser.getId())
        );
        hcUser.setWalletBalance(hcWallet.getAmount());
    }


    public void setOpenId(String code, String userId) throws WxErrorException {
        if (StringUtils.isBlank(code)) {
            throw new JeecgBootException("code empty");
        }
        log.info("setOpenId code:{}", code);
        WxOAuth2AccessToken accessToken = mpService.getOAuth2Service().getAccessToken(code);

        if (accessToken == null || StringUtils.isEmpty(accessToken.getOpenId())) {
            throw new JeecgBootException("获取用户信息失败");
        }

        this.updateById(new HcUser().setId(userId).setOpenId(accessToken.getOpenId()));
    }

    public void sendSms(String phone) throws UnsupportedEncodingException, ClientException {
        if (redisUtil.getExpire(RedisConstant.HC_LOGIN_USER_PHONE_CODE + phone) > 60 * 29) {
            throw new JeecgBootException("请不要重复发送验证码");
        }
        // 生成 100万以内的随机数，转换成字符串后，为6个字符的字符串
        String verificationCode = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
        JSONObject obj = new JSONObject();
        obj.put("code", verificationCode);

        //发送短信
        boolean b = DySmsHelper.sendSms(phone, obj, DySmsEnum.YST_LOGIN_TEMPLATE_CODE);

        if (!b) {
            throw new JeecgBootException("短信发送失败");
        } else {
            redisUtil.set(RedisConstant.HC_LOGIN_USER_PHONE_CODE + phone, verificationCode, 60 * 30);
        }
    }

    public HcSiteAddressDetails checkAddress(HcUser hcUser) {
        if (StringUtils.isBlank(hcUser.getAddressId())) {
            throw new JeecgBootException("请先选择地址");
        }

        HcSiteAddressDetails siteAddressDetails = hcSiteAddressDetailsService.getById(hcUser.getAddressId());

        boolean flag = true;

        if (siteAddressDetails == null) {
            flag = false;
        } else if (siteAddressDetails.getLatitude().compareTo(hcUser.getAddressLatitude()) != 0) {
            flag = false;
        } else if (siteAddressDetails.getLongitude().compareTo(hcUser.getAddressLongitude()) != 0) {
            flag = false;
        } else {
            boolean flagSupplyAddress = false;
            JSONArray jsonArray = JSON.parseArray(siteAddressDetails.getSupplyAddress());

            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String name = jsonObject.getString("name");
                String code = jsonObject.getString("code");
                String img = jsonObject.getString("img");

                if (name.equals(hcUser.getSupplyAddressName()) && code.equals(hcUser.getSupplyAddressCode())) {
                    flagSupplyAddress = true;
                    siteAddressDetails.setSupplyAddressImg(img);
                    siteAddressDetails.setSupplyAddressName(name);
                    siteAddressDetails.setSupplyAddressCode(code);
                    break;
                }
            }

            flag = flagSupplyAddress;
        }

        if (!flag) {
            this.update(new LambdaUpdateWrapper<HcUser>()
                    .set(HcUser::getAddressId, "")
                    .set(HcUser::getAddressLatitude, null)
                    .set(HcUser::getAddressLongitude, null)
                    .set(HcUser::getSupplyAddressName, "")
                    .set(HcUser::getSupplyAddressCode, "")
                    .eq(HcUser::getId, hcUser.getId())
            );

            throw new JeecgBootException("请先选择地址");
        }

        return siteAddressDetails;
    }

    /**
     *
     * @param code 微信用户的授权码，前端申请获取微信用户授权码时，会调用方法在微信客户端弹出一个弹窗，微信用户点击后才能从 微信服务上获取到授权码
     * @return
     * @throws WxErrorException
     */
    public HashMap<String, Object> loginForWx(String code) throws WxErrorException {
        if (StringUtils.isBlank(code)) {
            throw new JeecgBootException("code empty");
        }

        MiniAppSessoinEntity miniAppSessoinEntity =  code2Session(code);
        if (miniAppSessoinEntity == null || StringUtils.isEmpty(miniAppSessoinEntity.getOpenId())) {
            throw new JeecgBootException("获取用户信息失败");
        }
        // select * from hc_user where open_id = <openId> order by update_time desc limit 1;
        HcUser hcUser = this.getOne(new LambdaQueryWrapper<HcUser>().eq(HcUser::getOpenId, miniAppSessoinEntity.getOpenId())
                .orderByDesc(HcUser::getUpdateTime).last(" limit 1 ")
        );
        // 若表中没有，表示用户首次登陆，向hc_user表中添加记录，保存主要的信息：微信用户的 openid，phone
        if (hcUser == null) {
            hcUser = new HcUser().setOpenId(miniAppSessoinEntity.getOpenId()).setPhone("").setCreateBy("system").setCreateTime(new Date());
            this.save(hcUser);
        }
        // 基于微信用户的openid，生成 jwt 作为该用户后期调用接口的 token，JWT字符串格式：eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTkwOTYzMjIsInVzZXJuYW1lIjoiYWRtaW4ifQ.XuuK8AbuTnfwCtmbPsT_k25GCqflHiUjKrqGFujrgjU
        // JWT字符串由3部分组成：header.payload.signature；使用 点号 进行分隔
        // 生成token，格式为 {"alg":"HMAC256","typ":"JWT"}.{"username":"hc_user_login_token_<openid>"}.HMAC256(xxx);
        // JwtUtil.sign() 方法中，将jwt token的有效期，设置成了固定的 30天；没有将 token的有效期的设置，暴露在 sign() 方法的形参中
        String token = JwtUtil.sign(CommonConstant.HC_USER_LOGIN_TOKEN + hcUser.getOpenId(), CommonConstant.HC_USER_LOGIN_PASSWORD);

        // 设置token缓存有效时间； 每个token有效期为 60 天
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);

        HashMap<String, Object> tokenObject = new HashMap<>();
        tokenObject.put("token", token);
        return tokenObject;
    }

    /**
     * 根据用户临时授权code，获取openid 和 session_key （微信接口返回的数据都是加密的，需要使用此 session_key 进行解密）
     * @param code
     * @return
     */
    private MiniAppSessoinEntity code2Session(String code) {

        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);

        if (responseEntity !=null && responseEntity.getStatusCode() == HttpStatus.OK) {
            String sessionData = responseEntity.getBody();
            MiniAppSessoinEntity sessoinEntity = JSONObject.parseObject(sessionData, MiniAppSessoinEntity.class);
            return sessoinEntity;
        } else {
            throw new JeecgBootException("code2session failed");
        }
    }

    public HcUser login(String phone, String smsCode) {
        HcUser hcUser = this.getOne(new LambdaQueryWrapper<HcUser>().eq(HcUser::getPhone, phone));


        if (hcUser == null) {
            hcUser = new HcUser().setPhone(phone).setCreateBy("system").setCreateTime(new Date());
            this.save(hcUser);
        }
        Object redisVal = redisUtil.get(RedisConstant.HC_LOGIN_USER_PHONE_CODE + phone);

        if (redisVal == null) {
            throw new JeecgBootException("请先发送验证码");
        }

        if (!redisVal.toString().equals(smsCode)) {
            throw new JeecgBootException("手机验证码错误,请重新输入");
        }

        // 生成token
        String token = JwtUtil.sign(CommonConstant.HC_USER_LOGIN_TOKEN + hcUser.getPhone(), CommonConstant.HC_USER_LOGIN_PASSWORD);

        // 设置token缓存有效时间
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);

        HcUser result = new HcUser();
        result.setToken(token);
        return result;
    }
}
